package channel

import (
	"bytes"
	"encoding/binary"
	"errors"
	"go-iot-gateway/internal/apk/maths"
	"go-iot-gateway/internal/apk/types"
	channel_define "go-iot-gateway/internal/channel/define"
	"go-iot-gateway/internal/conf_table"
	conf_table_define "go-iot-gateway/internal/conf_table/define"
	"go-iot-gateway/internal/logger"
	storage_define "go-iot-gateway/internal/storage/define"
)

// WriteBytes 通道写值
func (manager *ChannelManager) WriteBytes(tagId string, bytes []byte) error {

	_, writableChannel, err := checkWriteTag(tagId, manager)
	if err != nil {
		return err
	}

	return writableChannel.WriteBytes(tagId, bytes)
}

// WriteValObject 通过storage_define.ValObject 写值
func (manager *ChannelManager) WriteValObject(tagId string, valObject storage_define.ValObject) error {
	tagInfo, writableChannel, err := checkWriteTag(tagId, manager)
	if err != nil {
		return err
	}

	var bytes_ []byte
	if float64Val, ok := valObject.(*storage_define.Float64Val); ok {
		bytes_, err = writeFloat64ValProcess(float64Val, tagInfo, false)
	} else if uint64Val, ok := valObject.(*storage_define.Uint64Val); ok {
		bytes_, err = writeUint64ValProcess(uint64Val, tagInfo, false)
	} else if int64Val, ok := valObject.(*storage_define.Int64Val); ok {
		bytes_, err = writeInt64ValProcess(int64Val, tagInfo, false)
	} else if stringVal, ok := valObject.(*storage_define.StringVal); ok {
		bytes_, err = writeStringValProcess(stringVal, tagInfo, false)
	} else {
		return errors.New("无效的ValObject类型")
	}

	if err != nil {
		return err
	}

	return writableChannel.WriteBytes(tagId, bytes_)
}

// writeStringValProcess 写字符串数据处理
func writeStringValProcess(val *storage_define.StringVal, tagInfo *conf_table_define.TagInfo, delegation bool) ([]byte, error) {

	if tagInfo.IsStringData() {
		logger.Connector().Infof("【写值】设备 %s  %s  %s\n", tagInfo.DeviceInfo.Dev_name, tagInfo.Tag_name, val.StringVal())
		return []byte(val.StringVal()), nil
	}

	if tagInfo.IsUintData() {
		toUint64, err := types.ToUint64(val.StringVal())
		if err != nil {
			return nil, err
		}
		return writeUint64ValProcess(storage_define.NewUint64Val(val.TagName(), toUint64, val.Ts(), val.Qos()), tagInfo, true)
	}

	if tagInfo.IsIntData() {
		toInt64, err := types.ToInt64(val.StringVal())
		if err != nil {
			return nil, err
		}
		return writeInt64ValProcess(storage_define.NewInt64Val(val.TagName(), toInt64, val.Ts(), val.Qos()), tagInfo, true)
	}

	if tagInfo.IsFloatData() {
		toFloat64, err := types.ToFloat64(val.StringVal())
		if err != nil {
			return nil, err
		}
		return writeFloat64ValProcess(storage_define.NewFloat64Val(val.TagName(), toFloat64, val.Ts(), val.Qos()), tagInfo, true)
	}

	return nil, errors.New("bcd类型目前不支持")

}

// writeInt64ValProcess 写整型数据处理
func writeInt64ValProcess(val *storage_define.Int64Val, tagInfo *conf_table_define.TagInfo, delegation bool) ([]byte, error) {

	int64Val := val.Int64Val()

	if !delegation {
		//todo 取反配置
		//todo 大小端处理
		//系数处理
		if tagInfo.Tag_K != 1 {
			_val := writeCoefficient(float64(int64Val), tagInfo.Tag_K, 0)
			int64Val = int64(_val)
		}

	}

	if tagInfo.IsIntData() {
		buf := new(bytes.Buffer)
		logger.Connector().Infof("【写值】设备 %s  %s  %d \n", tagInfo.DeviceInfo.Dev_name, tagInfo.Tag_name, int64Val)
		err := binary.Write(buf, binary.BigEndian, int64Val)
		if err != nil {
			return nil, err
		}
		return buf.Bytes(), nil
	}

	if tagInfo.IsStringData() {
		toString := types.ToString(int64Val)
		return writeStringValProcess(storage_define.NewStringVal(val.TagName(), toString, val.Ts(), val.Qos()), tagInfo, true)
	}

	if tagInfo.IsUintData() {
		toUint64, err := types.ToUint64(int64Val)
		if err != nil {
			return nil, err
		}
		return writeUint64ValProcess(storage_define.NewUint64Val(val.TagName(), toUint64, val.Ts(), val.Qos()), tagInfo, true)
	}

	if tagInfo.IsFloatData() {
		toFloat64, err := types.ToFloat64(int64Val)
		if err != nil {
			return nil, err
		}
		return writeFloat64ValProcess(storage_define.NewFloat64Val(val.TagName(), toFloat64, val.Ts(), val.Qos()), tagInfo, true)
	}

	return nil, errors.New("bcd类型目前不支持")
}

// writeUint64ValProcess 写uint 数据处理
func writeUint64ValProcess(val *storage_define.Uint64Val, tagInfo *conf_table_define.TagInfo, delegation bool) ([]byte, error) {
	uint64Val := val.Uint64Val()

	if !delegation {
		//todo 大小端处理
		//todo 取反配置
		//系数处理
		if tagInfo.Tag_K != 1 {
			_val := writeCoefficient(float64(uint64Val), tagInfo.Tag_K, 0)
			uint64Val = uint64(_val)
		}
	}

	if tagInfo.IsUintData() {
		buf := new(bytes.Buffer)
		logger.Connector().Infof("【写值】设备 %s  %s  %d \n", tagInfo.DeviceInfo.Dev_name, tagInfo.Tag_name, uint64Val)
		err := binary.Write(buf, binary.BigEndian, uint64Val)
		if err != nil {
			return nil, err
		}
		return buf.Bytes(), nil
	}

	if tagInfo.IsStringData() {
		toString := types.ToString(uint64Val)
		return writeStringValProcess(storage_define.NewStringVal(val.TagName(), toString, val.Ts(), val.Qos()), tagInfo, true)
	}

	if tagInfo.IsIntData() {
		toInt64, err := types.ToInt64(uint64Val)
		if err != nil {
			return nil, err
		}
		return writeInt64ValProcess(storage_define.NewInt64Val(val.TagName(), toInt64, val.Ts(), val.Qos()), tagInfo, true)
	}

	if tagInfo.IsFloatData() {
		toFloat64, err := types.ToFloat64(uint64Val)
		if err != nil {
			return nil, err
		}
		return writeFloat64ValProcess(storage_define.NewFloat64Val(val.TagName(), toFloat64, val.Ts(), val.Qos()), tagInfo, true)
	}

	return nil, errors.New("bcd类型目前不支持")
}

// writeFloat64ValProcess 写 float 数据处理
func writeFloat64ValProcess(val *storage_define.Float64Val, tagInfo *conf_table_define.TagInfo, delegation bool) ([]byte, error) {

	float64Val := val.Float64Val()

	if !delegation {
		//todo 取反配置
		//todo 大小端处理
		//系数处理
		if tagInfo.Tag_K != 1 {
			float64Val = writeCoefficient(float64Val, tagInfo.Tag_K, 4)
		}
	}

	if tagInfo.IsFloatData() {
		buf := new(bytes.Buffer)
		logger.Connector().Infof("【写值】设备 %s  %s  %v \n", tagInfo.DeviceInfo.Dev_name, tagInfo.Tag_name, float64Val)
		err := binary.Write(buf, binary.BigEndian, float64Val)
		if err != nil {
			return nil, err
		}
		return buf.Bytes(), nil
	}

	if tagInfo.IsStringData() {
		toString := types.ToString(float64Val)
		return writeStringValProcess(storage_define.NewStringVal(val.TagName(), toString, val.Ts(), val.Qos()), tagInfo, true)
	}

	if tagInfo.IsIntData() {
		toInt64, err := types.ToInt64(float64Val)
		if err != nil {
			return nil, err
		}
		return writeInt64ValProcess(storage_define.NewInt64Val(val.TagName(), toInt64, val.Ts(), val.Qos()), tagInfo, true)
	}

	if tagInfo.IsUintData() {
		toUint64, err := types.ToUint64(float64Val)
		if err != nil {
			return nil, err
		}
		return writeUint64ValProcess(storage_define.NewUint64Val(val.TagName(), toUint64, val.Ts(), val.Qos()), tagInfo, true)
	}

	return nil, errors.New("bcd类型目前不支持")
}

// 写系数计算
func writeCoefficient(val float64, k float32, places int) float64 {
	return maths.RoundPlaces(val*(1/float64(k)), places)
}

func checkWriteTag(tagId string, manager *ChannelManager) (*conf_table_define.TagInfo, channel_define.WritableChannel, error) {
	tagInfo, err := conf_table.FindAllTagByTagId(tagId)
	if tagInfo == nil || err != nil {
		return nil, nil, errors.New("点位不存在")
	}
	if !tagInfo.CanWrite() {
		return nil, nil, errors.New("点位是只读")
	}
	chlId := tagInfo.DeviceInfo.ChannelInfo.Chl_id
	channel := manager.GetChannelById(chlId)
	if channel == nil {
		return nil, nil, errors.New("通道不存在")
	}
	writableChannel, ok := channel.(channel_define.WritableChannel)
	if !ok {
		return nil, nil, errors.New("通道不可写")
	}
	return tagInfo, writableChannel, nil
}
